package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDto;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.MqConstants;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.IdUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.config.OrderStateMachine;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusChangeEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.job.OrderJob;
import com.jzo2o.health.mapper.OrdersCancelledMapper;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.OrderSnapshotDTO;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.*;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.rabbitmq.client.RabbitClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * TODO
 *
 * @Author whx
 * @Date 2024/12/13 21:56
 */
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {

    @Resource
    private ISetmealService setmealService;

    @Resource
    private IReservationSettingService reservationSettingService;

    @Resource
    private IMemberService memberService;

    @Resource
    private ReservationSettingMapper reservationSettingMapper;

    @Resource
    private IOrdersService owner;

    @Resource
    private RabbitClient rabbitClient;

    @Resource
    private NativePayApi nativePayApi;

    @Resource
    private TradeProperties tradeProperties;

    @Resource
    private OrdersCancelledMapper ordersCancelledMapper;

    @Resource
    private OrdersMapper ordersMapper;

    @Resource
    private TradingApi tradingApi;

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private OrderStateMachine orderStateMachine;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto) {

        // 套餐id
        Setmeal setmeal = setmealService.getById(dto.getSetmealId());
        if(ObjectUtil.isNull(setmeal)){
            throw new ForbiddenOperationException("该套餐不存在");
        }

        // 预约日期
        ReservationSetting rs = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .apply("number > reservations")
                .one();
        if(ObjectUtil.isNull(rs)){
            throw new ForbiddenOperationException("已经约满了老弟");
        }

        // 插入数据库
        Orders orders = BeanUtil.toBean(dto, Orders.class);
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());

        orders.setSetmealName(setmeal.getName());
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealRemark(setmeal.getRemark());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));

        Long userId = UserThreadLocal.currentUserId();

        Member member = memberService.getById(userId);

        orders.setMemberId(userId);
        orders.setMemberPhone(member.getPhone());

        orders.setSortBy(System.currentTimeMillis());

        this.save(orders);

        orderStateMachine.start(orders.getId().toString());

        // 当天预约+1
        ReservationSetting reservationSetting = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .one();

        if(ObjectUtil.isNull(reservationSetting)){
            throw new ForbiddenOperationException("无法预约这一天");
        }

        Integer row = reservationSettingMapper.updateReservations(reservationSetting.getId());
        if(row == 0){
            throw new DBException("无法预约这一天");
        }

        return new PlaceOrderResDTO(orders.getId());
    }

    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {

        Orders orders = this.getById(id);
        if(ObjectUtil.isNull(orders)){
            throw new ForbiddenOperationException("订单不存在");
        }

        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();

        // 通过支付渠道获取商家信息
        Long enterpriseId = ObjectUtils.equals(tradingChannel, PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId()
                : tradeProperties.getAliEnterpriseId();

        boolean changeChannel = false; //默认值：未更换
        if(ObjectUtils.isNotNull(orders.getTradingChannel()) && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel)) {
            changeChannel = true;
        }

        //业务系统唯一标识
        String productAppId = tradeProperties.getProductAppId();

        nativePayReqDTO.setEnterpriseId(enterpriseId);
        nativePayReqDTO.setProductAppId(productAppId);
        nativePayReqDTO.setProductOrderNo(id);
        nativePayReqDTO.setTradingChannel(tradingChannel);
        nativePayReqDTO.setTradingAmount(orders.getSetmealPrice());
        nativePayReqDTO.setMemo(orders.getSetmealName());
        nativePayReqDTO.setChangeChannel(changeChannel);

        // 生成订单
        NativePayResDTO response = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if(ObjectUtils.isNull(response)) {
            throw new ForbiddenOperationException("调用支付服务的支付接口失败");
        }

        OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
        orderSnapshotDTO.setTradingChannel(response.getTradingChannel());
        orderSnapshotDTO.setTradingOrderNo(response.getTradingOrderNo());

        orderStateMachine.changeStatus(orders.getId().toString(), OrderStatusChangeEnum.PAYED, orderSnapshotDTO);

        OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(response, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());

        return ordersPayResDTO;
    }

    @Override
    public List<OrdersResDTO> pageQuery(Integer ordersStatus, Long sortBy) {

        LambdaQueryWrapper<Orders> wrapper = Wrappers.<Orders>lambdaQuery()
                .lt(!ObjectUtil.isNull(sortBy), Orders::getSortBy, sortBy)
                .eq(!ObjectUtil.isNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .orderByDesc(Orders::getCreateTime);

        // 分页查询
        Page<Orders> page = PageDto.of(1, 10);

        // 分页查询
        IPage<Orders> resultPage = ordersMapper.selectPage(page, wrapper);

        List<Orders> records = resultPage.getRecords();

        return BeanUtil.copyToList(records, OrdersResDTO.class);
    }

    @Override
    public OrdersDetailResDTO detail(Long id) {

        Orders orders = this.getById(id);
        if(ObjectUtil.isNull(orders)){
            throw new ForbiddenOperationException("该订单不存在");
        }

        OrdersDetailResDTO response = BeanUtil.toBean(orders, OrdersDetailResDTO.class);

        // 是否已取消
        if(orders.getOrderStatus().equals(OrderStatusEnum.CANCELLED.getStatus())){
            OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(id);

            response.setCancelTime(ordersCancelled.getCancelTime());
            response.setCancelReason(ordersCancelled.getCancelReason());
        }

        return response;
    }

    @Override
    public PageResult<OrdersResDTO> pageQueryForAdmin(OrdersPageQueryReqDTO dto) {
        Page<Orders> page = PageDto.of(dto.getPageNo(), dto.getPageSize());

        this.lambdaQuery()
                .eq(!ObjectUtil.isNull(dto.getOrderStatus()), Orders::getOrderStatus, dto.getOrderStatus())
                .likeRight(!ObjectUtil.isNull(dto.getMemberPhone()), Orders::getMemberPhone, dto.getMemberPhone())
                .orderByDesc(Orders::getCreateTime)
                .page(page);

        PageResult<Orders> objectPageResult = new PageResult<>(page.getPages(), page.getTotal(), page.getRecords());

        return PageResult.of(objectPageResult, OrdersResDTO.class);
    }

    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        return ordersMapper.OrdersDetailAggregation(id);
    }

    @Override
    public OrdersCountResDTO countByStatus() {
        return new OrdersCountResDTO(ordersMapper.countByStatus());
    }

    @Override
    public OrdersPayResDTO payResult(Long id) {
        //1.判断订单是否存在
        Orders orders = this.getById(id);
        if(ObjectUtil.isNull(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }

        //2.判断是否有交易单号
        if(ObjectUtils.isNull(orders.getTradingOrderNo())) {
            throw new ForbiddenOperationException("非法订单");
        }

        //3.判断是否支付成功
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);
        if(ObjectUtils.equals(orders.getPayStatus(),OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            return ordersPayResDTO;
        }

        //4.调用支付服务Feign接口查询支付结果
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());

        //5.更新订单表信息（支付时间、第三方交易号、订单状态、支付状态）
        //update orders set pay_time=?,transaction_id=?,order_status=?,pay_status=? where id=?
        if(ObjectUtils.isNotNull(tradingResDTO) && ObjectUtils.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
            owner.paySuccess(id,tradingResDTO.getTransactionId(),tradingResDTO.getPaySuccessTime());

            //6.封装响应数据
            ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());//支付成功
        }
        return ordersPayResDTO;
    }

    @Override
    public void cancel(OrdersCancelReqDTO dto) {
        //1. 判断订单是否存在
        Orders orders = this.getById(dto.getId());
        if(orders==null) {
            throw new ForbiddenOperationException("订单不存在");
        }

        Integer status = orders.getOrderStatus();

        //2. 判断订单状态是否合法（必须是未支付或待体验）
        Integer noPayStatus = OrderStatusEnum.NO_PAY.getStatus();
        Integer waitingCheckupStatus = OrderStatusEnum.WAITING_CHECKUP.getStatus();
        List<Integer> statusList = Arrays.asList(noPayStatus, waitingCheckupStatus);
        if(!statusList.contains(status)) {
            throw new ForbiddenOperationException("非法状态");
        }

        //3. 如果是未支付 - 取消订单
        if(status.equals(noPayStatus)) {
            owner.cancelOrderByNoPay(dto.getId(), dto.getCancelReason(), false);
        } else {
            OrderJob orderJob = applicationContext.getBean(OrderJob.class);
            orderJob.cancelRefundOrder(orders, dto.getCancelReason(), UserContext.currentUser());
        }

    }

    @Override
    public void refund(OrdersCancelReqDTO dto) {
        Orders orders = this.getById(dto.getId());
        CurrentUserInfo currentUserInfo = UserThreadLocal.currentUser();
        OrderJob orderJob = applicationContext.getBean(OrderJob.class);
        orderJob.cancelRefundOrder(orders, dto.getCancelReason(), currentUserInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    public void cancelOrderByNoPay(Long orderId, String cancelReason, boolean isSystem) {
        //1. 添加取消记录
        OrdersCancelled ordersCanceled = new OrdersCancelled();
        ordersCanceled.setId(orderId);//订单id
        if(!isSystem) {
            ordersCanceled.setCancellerId(UserContext.currentUserId()); //取消人id
            ordersCanceled.setCancellerType(UserContext.currentUser().getUserType());//取消人类型
            ordersCanceled.setCancellerName(UserContext.currentUser().getName());//取消人姓名
        }
        ordersCanceled.setCancelReason(cancelReason);//取消原因
        ordersCanceled.setCancelTime(LocalDateTime.now());//取消时间
        int row = ordersCancelledMapper.insert(ordersCanceled);
        if(row == 0) {
            throw new DBException("添加取消记录失败");
        }

        //2. 更新订单状态 update orders set order_status=? where id=?
        orderStateMachine.changeStatus(orderId.toString(), OrderStatusChangeEnum.CANCEL);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long productOrderNo, String transactionId, LocalDateTime paySuccessTime) {
//        boolean result = this.lambdaUpdate()
//                .eq(Orders::getId, productOrderNo)
//                .set(Orders::getPayTime, paySuccessTime)
//                .set(Orders::getTransactionId, transactionId)
//                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
//                .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())
//                .update();
//        if(!result){
//            throw new DBException("更新订单失败");
//        }

        OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
        orderSnapshotDTO.setPayTime(paySuccessTime);
        orderSnapshotDTO.setTransactionId(transactionId);

        orderStateMachine.changeStatus(productOrderNo.toString(), OrderStatusChangeEnum.PAYED, orderSnapshotDTO);
    }
}
